package net.yxsoft.service.infrastructure;

import com.jfinal.kit.HashKit;
import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.activerecord.SqlPara;
import com.jfinal.plugin.ehcache.CacheKit;
import net.yxsoft.enumclass.TsUserTypeEnumCls;
import net.yxsoft.enumclass.WhetherEnumCls;
import net.yxsoft.kit.AESKit;
import net.yxsoft.kit.CheckPswMeter;
import net.yxsoft.kit.ToolDateTime;
import net.yxsoft.resource.AuthuserRes;
import net.yxsoft.resource.Res;
import net.yxsoft.model.infrastructure.TsMenu;
import net.yxsoft.model.infrastructure.TsSession;
import net.yxsoft.model.infrastructure.TsUser;
import net.yxsoft.websocket.MessageServer;
import rapid.core.Authuser;
import rapid.core.ContextStateEnum;
import rapid.core.TResult;
import rapid.jfinal.BaseService;

import java.lang.reflect.InvocationTargetException;
import java.sql.Date;
import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class LoginService extends BaseService {

  public static final LoginService INSTANCE = new LoginService();
  private final TsUser userDao = new TsUser().dao();

  public TResult login(String loginId, String password) throws Exception {
    return login(loginId, password, true, null);
  }

  public TResult login(String loginId, String password, boolean keepLogin) throws Exception {
    return login(loginId, password, keepLogin, null);
  }

  /**
  * @MethodName: login
  * @Description: 用户登录验证
  * @param: loginId
   * @param: password
   * @param: keepLogin
   * @param: userTypes
  * @return * @return rapid.core.TResult
  * @throws
  * @date 2023/8/31 13:15
  * @author wj
  */
  public TResult login(String loginId, String password, boolean keepLogin, TsUserTypeEnumCls[] userTypes) throws Exception {
    TsUser user = userDao.findFirst("select * from ts_user where login_id = ?",
                                    loginId);

    if(user == null) {
      return TResult.fail("账号不存在。");
    }

    if(userTypes != null) {
      boolean validType = false;

      for(TsUserTypeEnumCls type : userTypes) {
        if(user.getUserTyp().equals(type.getValue())) {
          validType = true;
          break;
        }
      }

      if(!validType) {
        return TResult.fail("账号不存在。");
      }
    }

    Date date = new Date(System.currentTimeMillis());



    if (user.isStatusLockId()) {
      return TResult.fail("账号已被锁定");
    }


    if(StrKit.notNull(user.getCanlogin())&&"0".equals(user.getCanlogin())) {
      return TResult.fail("账号禁止登陆");
    }
    
    String hashedPass = HashKit.sha256(loginId + AESKit.decrypt(password));
    System.out.println(hashedPass);

    // 未通过密码验证
    if(!user.getPassword().equals(hashedPass)) {

      return TResult.fail("密码不正确");
    }
    user.setLastLoginTime(new java.sql.Timestamp(new java.util.Date().getTime()));
    user.setLoginFailureTimes(0);
    user.update();
    // 如果用户勾选保持登录，暂定过期时间为 3 年，否则为 120 分钟，单位为秒
    long liveSeconds = keepLogin ? 24 * 60 * 60 : 120 * 60;
    // 传递给控制层的 cookie
    int maxAgeInSeconds = (int)(keepLogin ? liveSeconds : liveSeconds);
    // expireAt 用于设置 session 的过期时间点，需要转换成毫秒
    long expireAt = System.currentTimeMillis() + (liveSeconds * 1000);
    // 保存登录 session 到数据库

    TsSession session = new TsSession();
    String sessionId = StrKit.getRandomUUID();
    session.setId(sessionId);
    session.setUserId(user.getId());
    session.setExpireAt(expireAt);
    if(!session.save()) {
      return TResult.fail("保存 session 到数据库失败，请联系管理员");
    }

    user.removeSensitiveInfo();// 移除 password 与 salt 属性值
    user.put(AuthuserRes.KEY_TS_SESSION_UID, sessionId);

    Authuser auth = initAuthuser(user);

    CacheKit.put(Authuser.KEY_USER, sessionId, auth);

    /**
     * 加入Websocket在线
     */
    MessageServer.saveUser(auth);

    return TResult.success()
      .set(AuthuserRes.KEY_TS_SESSION_UID, sessionId)
      .set(Authuser.KEY_USER, auth)
      .set(Res.MAX_AGEIN_SECONDS, maxAgeInSeconds);   // 用于设置 cookie 的最大存活时间
  }




  /**
  * @MethodName: logout
  * @Description: 退出登录
  * @param: sessionId
  * @return * @return void
  * @throws
  * @date 2023/8/31 13:16
  * @author wj
  */
  public void logout(String sessionId) {
    if(StrKit.notBlank(sessionId)) {
      CacheKit.remove(Authuser.KEY_USER, sessionId);
      TsSession.dao.deleteById(sessionId);
    }
  }

  /**
   * @MethodName: findOneModuleAndApp
   * @Description: 查找用户可访问的模块和应用程序（不包括功能）---只有一级模块
   * @param: userId
   * @return * @return java.util.List<net.yxsoft.model.infrastructure.TsMenu>
   * @throws NoSuchMethodException
   * @throws SecurityException
   * @throws InvocationTargetException
   * @throws IllegalAccessException
   * @date 2023/8/31 13:17
   * @author wj
   */
  public List<TsMenu> findOneModuleAndApp(String userId)  {
    List<TsMenu> menus = new ArrayList<TsMenu>();
    List<TsMenu> parMenus = TsMenu.dao.find("SELECT ID, PID, NAME, PATH, SORT_ID FROM TS_MENU WHERE PID = 0 AND VISIBLE = ? AND ENABLED = ? ORDER BY SORT_ID ASC", WhetherEnumCls.YES.getValue(), WhetherEnumCls.YES.getValue());
    Kv params = Kv.create();

    if(StrKit.notBlank(userId)) {
      params.set("uid", userId);
    }

    for(TsMenu p : parMenus) {
      params.set("id", p.getId());
      SqlPara _sql =Db.getSqlPara("menu.findOneByPid", params);
      List<TsMenu> sub = TsMenu.dao.find(_sql);
      if(sub.size() > 0) {
        menus.add(p);
      }
    }
    return menus;
  }

  /**
  * @MethodName: findModuleAndApp
  * @Description: 查找用户可访问的模块和应用程序（不包括功能）
  * @param: userId
  * @return * @return java.util.List<net.yxsoft.model.infrastructure.TsMenu>
  * @throws NoSuchMethodException
  * @throws SecurityException
  * @throws InvocationTargetException
  * @throws IllegalAccessException
  * @date 2023/8/31 13:17
  * @author wj
  */
  public List<TsMenu> findModuleAndApp(String userId) throws NoSuchMethodException, SecurityException, InvocationTargetException, IllegalAccessException {
    List<TsMenu> menus = new ArrayList<TsMenu>();

    List<TsMenu> parMenus = TsMenu.dao.find("SELECT ID, PID, NAME, PATH, SORT_ID FROM TS_MENU WHERE PID = 0 AND VISIBLE = ? AND ENABLED = ? ORDER BY SORT_ID ASC", WhetherEnumCls.YES.getValue(), WhetherEnumCls.YES.getValue());
    Kv params = Kv.create();

    if(StrKit.notBlank(userId)) {
      params.set("uid", userId);
    }

    for(TsMenu p : parMenus) {
      params.set("pid", p.getId());

      SqlPara _sql =Db.getSqlPara("menu.findByPid", params);

      List<TsMenu> sub = TsMenu.dao.find(_sql);
      if(sub.size() > 0) {
        //Smenus.add(p);
        menus.addAll(sub);
      }
    }

    return menus;
  }

  /**
  * @MethodName: updateUserPwd
  * @Description: 修改用户密码
  * @param: loginId
   * @param: oldPwd
   * @param: newPwd
   * @param: confirmPwd
  * @return * @return rapid.core.TResult
  * @throws
  * @date 2023/8/31 13:17
  * @author wj
  */
  public TResult updateUserPwd(String loginId, String oldPwd, String newPwd, String confirmPwd) {
    TResult result = new TResult();

    //region 输入检测
    if(StrKit.isBlank(oldPwd)) {
      result.setState(ContextStateEnum.FAIL);
      result.setMessage("请填写原密码。");
      return result;
    }

    if(StrKit.isBlank(newPwd)) {
      result.setState(ContextStateEnum.FAIL);
      result.setMessage("请填写新密码。");
      return result;
    }

    /**
     * 密码复杂度检测
     */
    CheckPswMeter checkPswMeter = new CheckPswMeter(newPwd);
    if(checkPswMeter.CheckPswLength() < 32){

      return TResult.fail().msg("密码长度最低为8位");
    }

    if(checkPswMeter.CheckPswUpper() == 0 || checkPswMeter.CheckPwsLower() == 0 || checkPswMeter.checkNum() == 0){
      return TResult.fail().msg("密码中至少包含一个大小写字母加数字组合");
    }

    if (StrKit.isBlank(confirmPwd)) {

      return TResult.fail().msg("请再输入一遍新密码");
    }

    if (oldPwd.equals(newPwd)) {

      return TResult.fail().msg("新密码不可与原密码一样");
    }

    if (!newPwd.equals(confirmPwd)) {

      return TResult.fail().msg("两次输入的新密码不一致");
    }
    //endregion

    //region 更新密码
    TsUser user = TsUser.dao.findFirst("SELECT * FROM TS_USER WHERE LOGIN_ID=? AND PASSWORD=?", loginId,
                                       HashKit.sha256(loginId + oldPwd));

    if(user == null) {
      result.setState(ContextStateEnum.FAIL);
      result.setMessage("原密码不正确。");
      return result;
    }

    user.setPassword(HashKit.sha256(loginId + newPwd));

    if(!user.update()) {
      result.setState(ContextStateEnum.FAIL);
    }
    //endregion

    return result;
  }

  /**
  * @MethodName: initAuthuser
  * @Description:
  * @param: user
  * @return * @return rapid.core.Authuser
  * @throws
  * @date 2023/8/31 13:18
  * @author wj
  */
  public Authuser initAuthuser(TsUser user) {
    List<Record> groups = UserService.INSTANCE.findUserGroups(user.getId());

    Authuser auth = new Authuser();
    auth.setAdmin(true);
    auth.put(AuthuserRes.KEY_TS_SESSION_UID, user.get(AuthuserRes.KEY_TS_SESSION_UID));

    auth.setId(user.getId().toString());
    auth.setLoginId(user.getLoginId());
    auth.setName(user.getName());
    auth.put(AuthuserRes.KEY_PHONE, user.getPhone());
    auth.put(AuthuserRes.KEY_ROW_ID, user.getRowId());


    TsUserTypeEnumCls utype = null;

    if(StrKit.notBlank(user.getUserTyp())) {
      utype = TsUserTypeEnumCls.getObject(TsUserTypeEnumCls.class, user.getUserTyp());
    }

    auth.put(AuthuserRes.KEY_TYPE, utype);

    boolean hasRootGroup = false;
    StringBuilder userGroupIds = new StringBuilder();
    StringBuilder userGroupNames = new StringBuilder();

    int len = groups.size();
    int i = 0;

    for(Record temp : groups) {
      if(temp.getStr("id") == Res.groupRootId) {
        hasRootGroup = true;
        userGroupIds.append(temp.getInt("id"));
        userGroupNames.append(temp.getStr("name"));
        break;

      } else {
        i++;
        userGroupIds.append(temp.getStr("id"));
        userGroupNames.append(temp.getStr("name"));
        if(i < len) {
          userGroupIds.append(",");
          userGroupNames.append(",");
        }
      }
    }

    String groupIds = userGroupIds.toString();
    String groupNames = userGroupNames.toString();

    auth.put(AuthuserRes.KEY_GROUPS, groups);
    auth.put(AuthuserRes.KEY_GROUP_IDS, groupIds);
    auth.put(AuthuserRes.KEY_GROUP_NAMES, groupNames);
    auth.put(AuthuserRes.KEY_HAS_ROOT_GROUP, hasRootGroup);
    auth.put(AuthuserRes.KEY_ROLES, UserService.INSTANCE.findUserRoles(user.getId()));
    return auth;
  }

}
